import React, {
    useState,
    useEffect,
    useMemo,
    useCallback,
    memo,//包裹组件，性能优化
} from 'react';
import PropTypes, { func } from 'prop-types'
import './CitySelector.css'

// 改写类名组件
import classnames from 'classnames'


//三个组件
//1. 最小条目组件,只展示一条城市数据
// 用memo优化重渲染性能
const CityItem = memo(function CityItem(props) {
    const {
        name,
        onSelect,
    } = props;

    return (
        <li className="city-li" onClick={() => onSelect(name)} >
            {name}
        </li>
    )
})
CityItem.propTypes = {
    name: PropTypes.string.isRequired,
    onSelect: PropTypes.func.isRequired
}



//2. 包含同首字母开头的城市数据
const CitySection = memo(function CitySection(props) {
    const {
        title,
        cities = [],
        onSelect,
    } = props;

    return (
        <ul className="city-ul">
            <li className="city-li" key="title" data-cate={title}>
                {title}
            </li>
            {
                cities.map(city => {
                    return (
                        <CityItem
                            key={city.name}
                            name={city.name}
                            onSelect={onSelect}
                        />
                    )
                })
            }
        </ul>
    )
})
CitySection.propTypes = {
    title: PropTypes.string.isRequired,
    cities: PropTypes.array,//并不保证存在
    onSelect: PropTypes.func.isRequired,
}




//3. 最大子组件，包含所有数据
const CityList = memo(function CityList(props) {
    const {
        sections,
        onSelect,
        toAlpha
    } = props;

    return (
        <div className="city-list">
            <div className="city-cate">
                {
                    sections.map(section => {
                        return (
                            <CitySection
                                key={section.title}
                                title={section.title}
                                cities={section.cities}
                                onSelect={onSelect}
                            />
                        )
                    })
                }
            </div>
            <div className="city-index">
                {
                    alphaBet.map(alpha => {
                        return <AlphaIndex
                            key={alpha}
                            alpha={alpha}
                            onClick={toAlpha} />
                    })
                }
            </div>
        </div>
    )
})
CityList.propTypes = {
    sections: PropTypes.array.isRequired,
    onSelect: PropTypes.func.isRequired,
    toAlpha: PropTypes.func.isRequired,
}


//如何实现按字母定位？ 一个组件遍历26次即可
//第4个子组件，字母定位组件
const AlphaIndex = memo(function AlphaIndex(props) {
    const {
        alpha,
        onClick,
    } = props;

    return (
        <i className="city-item" onClick={() => onClick(alpha)}>
            {alpha}
        </i>
    )
})
AlphaIndex.propTypes = {
    alpha: PropTypes.string.isRequired,
    onClick: PropTypes.func.isRequired,
}

const alphaBet = Array.from(new Array(26), (ele, index) => {
    return String.fromCharCode(65 + index);
})




// 第5个子组件 搜索建议列表 
const SuggestItem = memo(function SuggestItem(props) {
    const {
        name,
        onClick,
    } = props;

    return (
        <li className="city-suggest-li" onClick={() => onClick(name)}>
            {name}
        </li>
    )
})
SuggestItem.propTypes = {
    name: PropTypes.string.isRequired,
    onClick: PropTypes.func.isRequired,
}

//第6个组件 SuggestItem的上游组件
const Suggest = memo(function Suggest(props) {
    const {
        searchKey,
        onSelect,
    } = props;


    const [result, setResult] = useState([]);

    // 根据searchKey的变化发起请求，并把请求结果渲染出来 副作用
    useEffect(() => {
        fetch('/rest/search?key=' + encodeURIComponent(searchKey))
            .then(res => res.json())//只需要关系成功，不需要考虑失败
            .then(data => {
                const {
                    result,
                    setSearchKey: sKey,
                } = data;

                // 做一个判断  可能有参数不同的请求在同时经行，避免旧的搜索结果
                if (sKey = searchKey) {
                    setResult(result)
                }
            })
    }, [searchKey])

    //如果搜索无结果？ 返回什么？ 直接展示搜索词searchKey
    //并用useMemo优化 优化意义不大
    // const fallBackResult=result.length?result:[{display:searchKey}]

    const fallBackResult = useMemo(() => {
        if (!result.length) {
            return [{
                display: searchKey,
            }]
        }
        return result
    }, [result, searchKey])


    return (
        <div className="city-suggest">
            <ul className="city-suggest-ul">
                {
                    fallBackResult.map(item => {
                        return (
                            <SuggestItem
                                key={item.display}
                                name={item.display}
                                onClick={onSelect}
                            />
                        )
                    })
                }
            </ul>
        </div>
    )
})
Suggest.propTypes = {
    searchKey: PropTypes.string.isRequired,
    onSelect: PropTypes.func.isRequired,
}





//主组件
const CitySelector = memo(function CitySelector(props) {
    const {
        show,
        cityData,
        isLoading,
        onBack,
        fetchCityData,
        onSelect
    } = props

    // 记录框内的初始值或入值，为清空而服务
    const [searchKey, setSearchKey] = useState('')

    // trim()删除头尾的字符串  使得即使输入一连串空白小叉子也不会出现
    const key = useMemo(() => searchKey.trim(), [searchKey]);


    // 异步加载数据对react来说是一种典型的副作用   副作用异步请求的同步问题
    useEffect(() => {
        if (!show || cityData || isLoading) {
            return;
        }
        fetchCityData()
    }, [show, cityData, isLoading])


    //字母定位函数
    const toAlpha = useCallback(alpha => {
        document.querySelector(`[data-cate='${alpha}']`)
            .scrollIntoView()
    }, [])


    const outPutCitySections = () => {
        //先判断，如果正在加载
        if (isLoading) {
            return <div>loading</div>
        }

        //加载完成
        if (cityData) {
            return (
                <CityList
                    sections={cityData.CityList}
                    onSelect={onSelect}
                    toAlpha={toAlpha}
                />
            )
        }
        //否则报错
        return <div>err</div>
    }



    return (
        <div className={classnames('city-selector', { hidden: !show })}>
            <div className="city-search">
                {/* 返回箭头 */}
                <div className="search-back" onClick={onBack}>
                    {/* 绘制返回箭头 */}
                    <svg width="42" height="42">
                        <polyline
                            points="25,13,16,21,25,29"
                            stroke="#fff"
                            strokeWidth="2"
                            fill="none"
                        />
                    </svg>
                </div>
                {/* 搜索框部分 */}
                <div className="search-input-wrapper">
                    <input
                        type="text"
                        value={searchKey}
                        className="search-input"
                        placeholder="城市、车站的中文或拼音"
                        onChange={e => setSearchKey(e.target.value)}
                    />
                </div>
                {/* 清空搜索框内容部分 只有当searchKey不为空时才显示 */}
                <i
                    onClick={() => setSearchKey('')}
                    className={classnames('search-clean', { hidden: key.length === 0 })}>
                    {/* 是一个字体图标常量 但为什么是在框内？不是并列吗 */}
                    &#xf063;
                </i>
            </div>
            {
                Boolean(key) && (
                    <Suggest
                        searchKey={key}
                        onSelect={key => onSelect(key)}
                    />
                )
            }
            {outPutCitySections}
        </div>
    )
})

// 公共组件，需要类型声明   应该放在函数组件之后，要不然会被认为没有初始化就使用报错
CitySelector.propTypes = {
    show: PropTypes.bool.isRequired,
    cityData: PropTypes.object,//不一定需要存在，比如初始化时
    isLoading: PropTypes.bool.isRequired,
    onBack: PropTypes.func.isRequired,
    fetchCityData: PropTypes.func.isRequired,
    onSelect: PropTypes.func.isRequired,
}


export default CitySelector;